module Explode.Editor;
private import Explode.GameState;
private import Explode.EditGameState;
private import Explode.Cursor;
private import Explode.GameTitleState;
private import Explode.GameoverState;
private import Explode.PauseMenuState;
private import Explode.Stage;
private import Explode.Player;
private import Explode.GameConfig;
private import Explode.StageSelector;
private import Form.MenuItem;
private import State.IState;
private import Input.KeyCode;
private import Input.GamePad;
private import Input.Mouse;
private import Input.InputRecorder;
private import Graphics.IGraphics;
private import Sound.Manager;
private import System.Console;
private import std.file;
private import std.string;
private import std.math;
private import std.format;

/**
*	エディット用
*/
public class EditorState : BasicState{
	protected IGraphics graphics;
	protected IGamePad _pad;
	protected BasicMouse _mouse;
	protected int frameCnt = 0;
	protected EditorStage _stage;
	protected IGraphics _graphics;
	protected int _zoom = 0;
	protected int _key_dn = 0 , _key_up = 0 , _key_lt = 0 , _key_rt = 0;
	protected bool _shiftKey , _altKey , _ctrlKey;
	protected EditGameState _gameState;
	
	protected GameParam _param;

	protected IState _testplay , _dying , _stageClear;
	protected GameTitleState _gameTitle;

	private bool _pauseFlag , _startFlag;
	private EditCursor _cursor;
	protected EditorPauseState _pausemenu;

	protected int _repeatCnt;
	public EditorStage stage(){ return _stage;}

	public this( GameTitleState title , IGraphics g ){
		_graphics = g;
		_gameTitle = title;
	} //

	public IState action(){
		frameCnt++;
		if ( _pad.x != 0 || _pad.y != 0 ) _repeatCnt++;
		else _repeatCnt = 0;
		if ( _repeatCnt % 6  == 1 || ( _repeatCnt > 18 ) ) {
			if ( _pad.x > 0 ) {
				if ( _shiftKey )		_stage.shiftRight( _ctrlKey );
				else	if ( _ctrlKey ) _stage.addRight();
				else					 _cursor.moveRight();
			}
			if ( _pad.x < 0 ) {
				if ( _shiftKey )		_stage.shiftLeft( _ctrlKey );
				else	if ( _ctrlKey ) _stage.delRight();
				else					_cursor.moveLeft();
			}
			
			if ( _pad.y > 0 ) {
				if ( _shiftKey )		_stage.shiftUp( _ctrlKey );
				else	if ( _ctrlKey ) _stage.addBottom();
				else					_cursor.moveUp();
			}
			if ( _pad.y < 0 ) {
				if ( _shiftKey )		_stage.shiftDown( _ctrlKey );
				else	if ( _ctrlKey ) _stage.delBottom();
				else					_cursor.moveDown();
			}
		}
		_stage.action();
			 if ( _zoom > 0 ) _stage.zoomIn();
		else if ( _zoom < 0 ) _stage.zoomOut();


		if ( _pad.getButton( 8 ) != 0 ) {
			_startFlag = true;
		} //
		
		if ( _startFlag ) return startStage();



		if ( _pauseFlag ) return _pausemenu.call( this );
		return this;
	} //

	public IState draw( IGraphics g ) {
		g.clear();
			_stage.setViewport( g );
			_stage.drawingEntry( g );
		g.draw();
		return this;
	} //

	public IState call( IState caller ){
		initObject();
		_pad.reset();
		_zoom   = 0;
		_key_dn = 0;
		_key_up = 0;
		_key_lt = 0;
		_key_rt = 0;
		_pauseFlag = false;
		_startFlag = false;
		_shiftKey = _altKey = _ctrlKey = false;
		_repeatCnt = 0;
		SoundManager.background.stop();
		return nextCall( caller );
	} //
	
	protected void initObject(){
		if ( _stage is null ) {
			_gameState	   = new EditGameState( this , _graphics );
			_pad		   = new VirtualGamePad( 8 );
			_mouse		   = new BasicMouse();
			_param		   = new GameParam();
			_stage		   = new EditorStage( _graphics , _param );
			_stage.gamepad = _pad;
			_stage.mouse   = _mouse;
			_stage.loadDefaultStage();
			_cursor		   = _stage.cursor;
			_pausemenu     = new EditorPauseState( this , _param , _pad , _graphics );
			_param.reset();
		}
	} //

	protected IState startStage() {
		_gameState.call( this );
		if ( _stage.changed ) {
			_gameState.loadStage( _stage.mapData );
		}else {
			_gameState.loadStageFromFile( _pausemenu.fileName );
		}
		return _gameState;
	} //

	protected IState nextCall( IState caller ) {
		if ( caller is _pausemenu ) {
			int rt = _pausemenu.returnValue;
			switch ( rt ) {
				case EditorPauseState.PLAY:
					return startStage();
				case EditorPauseState.TITLE:
					return _gameTitle.call( this );
				case EditorPauseState.CLS:
					_stage.cls();
					break;
				case EditorPauseState.LOAD:
					_stage.loadStageFromFile( _pausemenu.fileName );
					break;
				case EditorPauseState.SAVE:
					_stage.saveStageToFile( _pausemenu.fileName );
					break;
				default:
					return this;
			}
		} else
		if ( caller is null ) {
			return _gameTitle.call( this );
		} else
		if ( caller is _gameTitle ) {
			return this;
		} else
		if ( caller is _stageClear ) {
			return this;
		} else
		if ( caller is _dying ) {
			return this;
		}
		return this;
	} //



	public bool onMouseMove( int x , int y , int button) {
		_mouse.x = x;
		_mouse.y = y;
		return true;
	} //
	public bool onMouseDown( int x , int y , int button) {
		_mouse.button[ button ] = true;
		return true;
	} //
	public bool onMouseUp( int x , int y , int button) {
		_mouse.button[ button ] = false;
		return true;
	} //
	public bool onMouseWheel( int x , int y , int delta) {
		if( _shiftKey ) {
			if ( delta < 0 ) _stage.zoomIn();
			 else 			 _stage.zoomOut();
		} else {
			if ( delta < 0 ) _mouse.delta =  1;
			else			 _mouse.delta = -1;
		}
		return true;
	} //

	public bool onKeyDown( int keyCode ){
		switch ( keyCode ) {
			case KeyCode.UP:
				_key_up = -1;
				_pad.y = -1;
				break;
			case KeyCode.DOWN:
				_key_dn =  1;
				_pad.y =  1;
				break;
			case KeyCode.LEFT:
				_key_lt = -1;
				_pad.x = -1;
				break;
			case KeyCode.RIGHT:
				_key_rt =  1;
				_pad.x =  1;
				break;
			case KeyCode.Z:
				_pad.setButton( 0 , 1 );
				break;
			case KeyCode.X:
				_pad.setButton( 1 , 1 );
				break;
			case KeyCode.PAGEDOWN:
				if ( _shiftKey ) _zoom = -1;
				else 			_cursor.setPreviousBlock();
				break;
			case KeyCode.PAGEUP:
				if ( _shiftKey ) _zoom =  1;
				else 			_cursor.setNextBlock();
				break;
			case KeyCode.DELETE:
			case KeyCode.ENTER:
				_startFlag = true;
				break;
			case KeyCode.HOME:
				_zoom =  0;
				break;
			case KeyCode.RSHIFT:
			case KeyCode.LSHIFT:
				_shiftKey = true;
				break;
			case KeyCode.RALT:
			case KeyCode.LALT:
				_altKey   = true;
				break;
			case KeyCode.RCTRL:
			case KeyCode.LCTRL:
				_ctrlKey  = true;
				break;
			case KeyCode.K0:case KeyCode.T0:
				_cursor.setIndex( 0 );	_pad.setButton( 0 , 1 );
				break;
			case KeyCode.K1:case KeyCode.T1:
				_cursor.setIndex( 1 );	_pad.setButton( 0 , 1 );
				break;
			case KeyCode.K2:case KeyCode.T2:
				_cursor.setIndex( 2 );	_pad.setButton( 0 , 1 );
				break;
			case KeyCode.K3:case KeyCode.T3:
				_cursor.setIndex( 3 );	_pad.setButton( 0 , 1 );
				break;
			case KeyCode.K4:case KeyCode.T4:
				_cursor.setIndex( 4 );	_pad.setButton( 0 , 1 );
				break;
			case KeyCode.K5:case KeyCode.T5:
				_cursor.setIndex( 5 );	_pad.setButton( 0 , 1 );
				break;
			case KeyCode.K6:case KeyCode.T6:
				_cursor.setIndex( 6 );	_pad.setButton( 0 , 1 );
				break;
			case KeyCode.K7:case KeyCode.T7:
				_cursor.setIndex( 7 );	_pad.setButton( 0 , 1 );
				break;
			case KeyCode.K8:case KeyCode.T8:
				_cursor.setIndex( 8 );	_pad.setButton( 0 , 1 );
				break;
			case KeyCode.K9:case KeyCode.T9:
				_cursor.setIndex( 9 );	_pad.setButton( 0 , 1 );
				break;
			case KeyCode.A:
				_cursor.setIndex( 10 );	_pad.setButton( 0 , 1 );
				break;
			case KeyCode.B:
				_cursor.setIndex( 11 );	_pad.setButton( 0 , 1 );
				break;
			case KeyCode.C:
				_cursor.setIndex( 12 );	_pad.setButton( 0 , 1 );
				break;
			default:
				break;
		}
		return true;
	} //
	public bool onKeyUp( int keyCode ){
		switch ( keyCode ) {
			case KeyCode.UP:
				_key_up = 0;
				_pad.y = _key_dn;
				break;
			case KeyCode.DOWN:
				_key_dn = 0;
				_pad.y = _key_up;
				break;
			case KeyCode.LEFT:
				_key_lt = 0;
				_pad.x = _key_rt;
				break;
			case KeyCode.RIGHT:
				_key_rt = 0;
				_pad.x = _key_lt;
				break;
			case KeyCode.Z:
				_pad.setButton( 0 , 0 );
				break;
			case KeyCode.X:
				_pad.setButton( 1 , 0 );
				break;
			case KeyCode.PAGEDOWN:
				_zoom = 0;
				break;
			case KeyCode.PAGEUP:
				_zoom = 0;
				break;
			case KeyCode.SPACE:
				_pauseFlag = true;
				break;
			case KeyCode.RSHIFT:
			case KeyCode.LSHIFT:
				_shiftKey = false;
				break;
			case KeyCode.RALT:
			case KeyCode.LALT:
				_altKey   = false;
				break;
			case KeyCode.RCTRL:
			case KeyCode.LCTRL:
				_ctrlKey  = false;
				break;
			case KeyCode.K0:case KeyCode.T0:
			case KeyCode.K1:case KeyCode.T1:
			case KeyCode.K2:case KeyCode.T2:
			case KeyCode.K3:case KeyCode.T3:
			case KeyCode.K4:case KeyCode.T4:
			case KeyCode.K5:case KeyCode.T5:
			case KeyCode.K6:case KeyCode.T6:
			case KeyCode.K7:case KeyCode.T7:
			case KeyCode.K8:case KeyCode.T8:
			case KeyCode.K9:case KeyCode.T9:
			case KeyCode.A:case KeyCode.B:
			case KeyCode.C:
				_pad.setButton( 0 , 0 );
				break;
			default:
				break;
		}
		return true;
	} //
	public bool onJoyAxis( int deviceId , int axis , int delta ){
		if ( axis == JOYAXIS_X ) {
			if( delta != 0 ) _pad.x = ( delta > 0 ) ? 1 : -1;
			else			 _pad.x = 0;
		}
		if ( axis == JOYAXIS_Y ){
			if( delta != 0 ) _pad.y = ( delta > 0 ) ? 1 : -1;
			else			 _pad.y = 0;
		}
		return true;
	} //
	
	public bool onJoyButtonDown( int id , int button ) {
		int bm = GameConfig.getButtonMap( button );
		if ( bm == 9 ) _pauseFlag = true;
		else
			_pad.setButton( bm , 1 );
		return true;
	} //

	public bool onJoyButtonUp( int id , int button ) {
		_pad.setButton( GameConfig.getButtonMap( button ) , 0 );
		return true;
	} //
	
	
	
} //


public class EditorStage : BaseStage {
	protected EditCursor _cursor;
	protected IGamePad _pad;
	protected BasicMouse _mouse;
	protected GameParam _param;
	protected bool _changed;
	protected int _goldCnt;
	public bool changed() {
		return _changed;
	} //
	public void  gamepad( IGamePad g ) {
		_pad = g;
	} //
	public void  mouse( BasicMouse m ) {
		_mouse = m;
	} //
	
	public EditCursor cursor() {
		return _cursor;
	} //

	public this( IGraphics g , GameParam p ){
		super( g );
		_graphics = g;
		_param	  = p;

		initImage( );
		initObject( g , p );
	} //
	protected void initObject( IGraphics g , GameParam p ) {
		_mapTexture		= GameConfig.mapTexture;
		_playerTexture	= GameConfig.playerTexture;
		_enemyTexture	= GameConfig.enemyTexture;
		_missSound		= GameConfig.missSound;
		_digSound		= GameConfig.digSound;
		_goalSound		= GameConfig.goalSound;
		_goldSound		= GameConfig.goldSound;

		_cursor  = new EditCursor( this , g );
		_datStr = "";
		_changed  = true;
	} //

	
	protected bool loadStage( string src ) {
		bool b = super.loadStage( src ) ;
		int gw = _graphics.screenWidth , gh = _graphics.screenHeight;
		_cursor.moveCenter();
		setAspectRatio();
		_vWidth     = getParam( "vScreenWidth"  , _vWidth );
		_vHeight    = getParam( "vScreenHeight" , _vHeight );
		for ( int y = 1 ; y < _rows -1 ; y++ ) {
			for ( int x = 1 ; x < _cols - 1 ; x++ ) {
				if ( _dat[y][x] == Blocks.PLAYER ) _cursor.moveTo( x * Blocks.WIDTH , y * Blocks.HEIGHT );
			}
		}
		_changed = false;
		return b;
	} //
	
	public bool loadDefaultStage() {
		bool b = loadStageFromFile( GameConfig.stageDir ~ "/edit0000.txt" );
		initImage();
		_changed = true;
		return b;
	} //
	
	
	protected void reset() {
		super.reset();
		setAspectRatio();
		_cursor.reset();
		_goldCnt = 0;
	} //

	public bool drawingEntry( IGraphics g ) {
		_cursor.drawingEntry( g );
		g.addDrawingEntry( this ) ;
		return true;
	} //

	public void setViewport( IGraphics g ) {
		g.setViewport(	  _scrollLeft
						, _scrollTop
						, _vWidth  + _scrollLeft
						, _vHeight + _scrollTop
						);
	} //

	public bool draw( IGraphics g ) {
		alias Blocks B;
		float clipX  = g.viewrect.x  , clipY  = g.viewrect.y , 
			  clipX2 = g.viewrect.x2 , clipY2 = g.viewrect.y2;

		setViewport( g );


		int vw = _vWidth;
		int vh = _vHeight;
		int sx = _scrollLeft >> B.SHIFTX;
		int sy = _scrollTop  >> B.SHIFTY;
		int dw = vw / B.WIDTH  + sx + 2;
		int dh = vh / B.HEIGHT + sy + 2;
		
		if ( dw > _cols ) dw = _cols;
		if ( dh > _rows ) dh = _rows;
		if ( sx < 0 ) sx = 0;
		if ( sy < 0 ) sy = 0;
		Color gridColor = _changed ? Color.create( 0x33ff0000 ) : Color.create( 0x33666666 );
		for ( int y = sy ; y < dh ; y++ ) {
			for ( int x = sx ; x < dw ; x++ ) {
				if ( B.isDigger( _dat[y][x] ) ) continue;
				int imgIndex = B.imageIndex( _dat[y][x] );
				float drawX = x * B.WIDTH ;
				float drawY = y * B.HEIGHT;
				if ( imgIndex != 0 ) {
					_texture.frameIndex = imgIndex * 4  + ( ( _frameCnt / 32 ) % 4 ) ;
					g.drawImage( _texture , drawX , drawY , B.WIDTH , B.HEIGHT );
				} else
					g.drawRect( drawX , drawY , B.WIDTH , B.HEIGHT , gridColor );
			}
		}

		g.setViewport( 0 , 0 , 640 , 480 );
		string top    = format("SCREEN %4dx%4d" , _vWidth , _vHeight);
		string bottom = format("SIZE %02dx%02d      CURSOR %02dx%02d" , _cols - 2 , _rows - 2 , _cursor.bx , _cursor.by );
		Color c = Color.create( 0x80ffff00 ) , bg = Color.create( 0x80000000 );
		g.drawFilledRect(0 ,           0 , 640 , 40 , bg );
		g.drawFilledRect(0 , 16 * 28 - 2 , 640 , 40 , bg );
		g.drawText( top    , (640 - top.length    * 16 ) / 2 ,           0 , c , 16 , 32 );
		g.drawText( bottom , (640 - bottom.length * 16 ) / 2 , 16 * 28 - 2 , c , 16 , 32 );

		g.setViewport( clipX , clipY , clipX2 , clipY2);
		return true;
	} //

	public bool action() {
		_frameCnt++;
		cursorAction();
		super.setScrollPoint( _cursor );
		return true;
	} //

	protected void cursorAction() {
		alias Blocks B;
		alias _graphics g;
		_cursor.action();
		if ( _mouse.motionX != 0 || _mouse.motionY != 0 ) {
			float xp = 1f * _mouse.x / g.screenWidth;
			float yp = 1f * _mouse.y / g.screenHeight;
			float mx = _vWidth  * xp + _scrollLeft;
			float my = _vHeight * yp + _scrollTop;
			int xx = ( cast(int)round(mx) >> B.SHIFTX ) << B.SHIFTX;
			int yy = ( cast(int)round(my) >> B.SHIFTY ) << B.SHIFTY;

			_cursor.moveTo( xx , yy );
		}
		int dy = _mouse.deltaY;
		if ( dy != 0 ) {
			if ( dy < 0 ) _cursor.setNextBlock();
			else		  _cursor.setPreviousBlock();
		}
		int bk = _cursor.currentBlock;
		int cbx = _cursor.bx , cby = _cursor.by;
		int cb  = getXYBlock( cbx , cby );
		if ( _mouse.button[1] || _mouse.button[2] || _pad.getButton(0) > 0 || _pad.getButton(1) > 0 ) {
			if ( _mouse.button[2] || _pad.getButton(1) > 0 ) bk = B.BLANK;
			switch ( bk ) {
				case B.BLANK:
				case B.BRICK:
				case B.ROCK:
				case B.LADDER:
				case B.ROPE:
				case B.HHOLE:
				case B.HLADDER:
				case B.ENEMY:
				case B.PLAYER:
				case B.MBLOCK:
				case B.GOAL:
					if ( bk != cb ) {
						if ( B.isMatch( cb , bk ) ) {
							_goldCnt--;
						}
						_changed = true;
						setXYBlock( cbx , cby , bk );
					}
					break;
				case B.GOLD:
					if ( bk != cb ) {
						if ( !B.isMatch( cb , bk ) ) {
							_goldCnt++;
						}
						_changed = true;
						setXYBlock( cbx , cby , bk );
					}
					break;
				default:
					break;
			}
		}
	} //


	public void zoomIn() {
		int vw = _vWidth  - ( _vWidth  >> 5 );
		int vh = _vHeight - ( _vHeight >> 5 );
		if ( vw < ( Blocks.WIDTH * 3 ) ) { // || ( vh < ( Blocks.HEIGHT * 1 ) ) ) {
			return;
		}
		_vWidth  = vw;
		_vHeight = vh;
	} //

	public void zoomOut() {
		int vw = _vWidth  + ( _vWidth  >> 5 );
		int vh = _vHeight + ( _vHeight >> 5 );
		if ( vw >= _vWidthMax || vh >= _vHeightMax ) {
			vw = _vWidthMax;
			vh = _vHeightMax;
		}
		_vWidth  = vw;
		_vHeight = vh;
	} //

	public void setScrollPoint( int sx , int sy ){
		int vw = _vWidth; 
		int vh = _vHeight;
		if( vw < _width ) {
			_scrollLeft = sx;
			_scrollLeftMax = _width  - Blocks.HALFWIDTH  - _vWidth;
			_scrollLeftMin = Blocks.HALFWIDTH;
			if ( _scrollLeft > _scrollLeftMax ) _scrollLeft = _scrollLeftMax;
			if ( _scrollLeft < _scrollLeftMin ) _scrollLeft = _scrollLeftMin;
		}else{
			_scrollLeft = ( ( _width - vw ) / 2 );
		}

		if ( vh < _height ) {
			_scrollTop  = sy;
			_scrollTopMax  = _height - Blocks.HALFHEIGHT - _vHeight;
			_scrollTopMin  = Blocks.HALFHEIGHT;
			if ( _scrollTop  > _scrollTopMax ) _scrollTop  = _scrollTopMax;
			if ( _scrollTop  < _scrollTopMin ) _scrollTop  = _scrollTopMin;
		} else {
			_scrollTop = ( ( _height - vh ) / 2 );
		}
	} //

	public void setAspectRatio() {
		_width    = _cols * Blocks.WIDTH;
		_height   = _rows * Blocks.HEIGHT;
		_vWidth   = _width  - Blocks.WIDTH;
		_vHeight  = _height - Blocks.HEIGHT;
		int gw = _graphics.screenWidth , gh = _graphics.screenHeight;
		float sx = gw * 1f / _vWidth , sy = gh * 1f / _vHeight;
		if ( sx < sy )
			_vHeight = cast(int)round( gh * 1f / sx );
		else
			_vWidth  = cast(int)round( gw * 1f / sy );
		_vWidthMax   = _vWidth;
		_vHeightMax  = _vHeight;
		// はみ出しチェック
		_cursor.moveTo( cast(int)_cursor.x , cast(int)_cursor.y );
	}//


	public void addRight() {
		for ( int y = 0 ; y < _rows ; y++ ) {
			if ( y > 0 && y < _rows - 1 ) _dat[y][_cols - 1] = Blocks.BLANK;
			_dat[ y ] ~= Blocks.ROCK;
		}
		_cols ++;
		setAspectRatio();
		_changed  = true;
	} //

	public void addBottom() {
		int[] newline;
		for ( int x = 0 ; x < _cols ; x++ ) {
			newline ~= Blocks.ROCK;
			if ( x > 0 && x < _cols - 1 ) _dat[_rows-1][x] = Blocks.BLANK;
		}
		_dat ~= newline;
		_rows ++;
		setAspectRatio();
		_changed  = true;
	} //

	public void delRight() {
		if ( _cols <= 4 ) return ;
		for ( int y = 0 ; y < _rows ; y++ ) {
			_dat[ y ].length = _cols - 1;
			_dat[ y ][ _cols - 2 ] = Blocks.ROCK;
		}
		_cols --;
		setAspectRatio();
		_changed  = true;
	} //

	public void delBottom() {
		if ( _rows <= 4 ) return ;
		_dat[ _rows - 2 ] = _dat[ _rows - 1 ];
		_dat.length  = _dat.length - 1;
		_rows--;
		setAspectRatio();
		_changed  = true;
	} //
	
	
	public void shiftRight( bool rotate ) {
		for ( int y = 1 ; y < _rows - 1 ; y++ ) {
			int tmp = _dat[y][ _cols - 2 ];
			for ( int x = _cols - 3 ; x >= 1 ; x-- ) {
				_dat[y][x+1] = _dat[y][x];
			}
			if ( rotate ) _dat[y][1] = tmp;
			else		  _dat[y][1] = Blocks.BLANK;
		}
		_changed  = true;
	} //
	public void shiftLeft( bool rotate ) {
		for ( int y = 1 ; y < _rows - 1 ; y++ ) {
			int tmp = _dat[y][ 1 ];
			for ( int x = 2 ; x < _cols - 1 ; x++ ) {
				_dat[y][x-1] = _dat[y][x];
			}
			if ( rotate ) _dat[y][ _cols - 2 ] = tmp;
			else		  _dat[y][ _cols - 2 ] = Blocks.BLANK;
		}
		_changed  = true;
	} //

	public void shiftUp( bool rotate ) {
		int[] tmp = _dat[_rows - 2 ];
		for ( int y = _rows - 3 ; y >= 1 ; y-- ) {
			_dat[y+1] = _dat[y];
		}
		if ( !rotate ) {
			for ( int x = 1 ; x < _cols - 1 ; x++ ) tmp[x] = Blocks.BLANK;
		}
			_dat[1] = tmp;
		_changed  = true;
	} //

	public void shiftDown( bool rotate ) {
		int[] tmp = _dat[ 1 ];
		for ( int y = 2 ; y < _rows - 1 ; y++ ) {
			_dat[y - 1] = _dat[y];
		}
		if ( !rotate ) {
			for ( int x = 1 ; x < _cols - 1 ; x++ ) tmp[x] = Blocks.BLANK;
		}
		_dat[ _rows - 2 ] = tmp;	
		_changed  = true;
	} //

	public void cls( int fill = Blocks.BLANK ) {
		Console.writeln("cls!");
		for ( int y = 1 ; y < _rows - 1 ; y++ ) {
			for ( int x = 1 ; x < _cols - 1 ; x++ ) {
				_dat[ y ][ x ] = fill;
			}
		}
		_changed  = true;
	} //
	
	public string mapData() {
		char toc( int i ) {
			string s="0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
			return s[i];
		}
		alias std.string.toString tos;
		string buf[];
		buf ~= ["[body]\n"];
		for ( int y = 1 ; y < _rows - 1 ; y++ ) {
			string ln = "";
			for ( int x = 1 ; x < _cols - 1 ; x++ ) {
				ln ~= toc( Blocks.imageIndex( _dat[y][x] ) );
			}
			ln ~= "\n";
			buf ~= ln;
		}
		buf ~= ["[param]\n"];
		buf ~= ["vScreenWidth=",tos(_vWidth),"\n"];
		buf ~= ["vScreenHeight=",tos(_vHeight),"\n"];
		return join( buf , "" );
	} //
	
	public bool saveStageToFile( string fileName ) {
		try{
			string tmpFile = fileName ~ ".tmp";
			string data = mapData;
			if ( exists( tmpFile ) ) std.file.remove( tmpFile );
			write( tmpFile , mapData );
			if ( exists( fileName ) ) std.file.remove( fileName );
			rename( tmpFile , fileName );

			// リプレイファイルがあれば消す
			string[] filetmp = split( fileName , "/" );
			string dir = join( filetmp[0..length-1] , "/" ) ~"/";
			string replayFile = dir ~ "replay/" ~ filetmp[ filetmp.length - 1 ];
			if ( exists( replayFile ) ) std.file.remove( replayFile );

			_changed = false;
			return true;
		} catch ( Object e ) {
			Console.writeln("FileException:%s" , e.toString() );
			return false;
		}
	} //
	
} //



/**
*	PAUSE画面用
*/
private class EditorPauseState : BasicState {
	public const int CANCEL      = 0;
	public const int RETURN      = 2;
	public const int TITLE       = 3;

	public const int CLS         = 100;
	public const int SAVE        = 101;
	public const int LOAD        = 102;
	public const int PLAY        = 200;

	protected int _returnValue = 0;
	public int returnValue(){return _returnValue;}

	protected string _fileName = "";
	public string fileName(){return _fileName;}

	protected const int WIDTH = 320 , HEIGHT = 400;

	private class Background : IDrawingEntry{
		public float x(){return 0;}
		public float y(){return 0;}
		public float z(){return 200;}
		public bool drawingEntry( IGraphics g ){
			return g.addDrawingEntry( this );
			
		} //
		public bool draw( IGraphics g ){
			g.setViewport( 0 , 0, WIDTH , HEIGHT );
			g.drawFilledRect( 0 , 0 , WIDTH , HEIGHT , Color.create(0x08000000u) );
			if ( _frameCnt / 30 % 3 > 0 ) {
			string txt = "P A U S E";
			int ww = 32 , hh = 64;
				g.drawText( txt , ( WIDTH - ww * txt.length )/2 , 70 , Color.create( 0x66ffffffu ) , ww , hh );

				g.drawText( txt , ( WIDTH - ww * txt.length )/2 , 50 , Color.create( 0xccff0000u ) , ww , hh );
			}
//			g.drawFilledRect( 64 , HEIGHT / 2 + 80 , WIDTH - 128 ,  80 , Color.create( 0x330000ffu ) , 0 );
			return true;
		} //
	} //


	private GameParam _param;
	private IState _caller;
	private int _frameCnt;
	protected IGamePad _pad;
	protected IMenuItem[] _items; 
	protected MenuItem _returnMenu , _playMenu , _titleMenu , _saveMenu , _loadMenu , _clsMenu;
	
	protected EditorState _editorState;
	protected StageSelectState _selectState;

	protected IMenuItem _activeItem ;
	protected IState _nextState ;
	protected IGraphics _graphics;

	protected string[] _stageValues;
	protected Background _background;
	


	public this( EditorState state , GameParam param , IGamePad pad , IGraphics g ){
		_editorState = state;
		_param     = param;
		_pad   = pad;
		_graphics  = g;
	} //

	public IState call( IState caller ) {
		if ( caller is _selectState ) {
			if ( _selectState.returnValue == StageSelectState.SELECT ) {
				_fileName = _selectState.fileName;
				return _editorState.call ( this );
			}
		}
		_caller = caller;
		_nextState = this;
		_pad.reset();
		initMenu();
		_frameCnt = 0;
		_returnValue = 0;
		return this;
	} //
	
	private void initMenu() {
		if ( _returnMenu is null ) {
			Console.write("Create EditorPauseMenu...");
			_selectState = new StageSelectState( GameConfig.stageDir , _pad , _graphics );
			_background = new Background();
			_playMenu  = new MenuItem("PLAY");
			_saveMenu  = new MenuItem("SAVE");
			_loadMenu  = new MenuItem("LOAD");
			_clsMenu   = new MenuItem("CLEAR");
			_returnMenu  = new MenuItem("CANCEL");
			_titleMenu   = new MenuItem("RETURN TO TITLE");
			
			_items ~= _playMenu;
			_items ~= _saveMenu;
			_items ~= _loadMenu;
			_items ~= _clsMenu;
			_items ~= _titleMenu;
			_items ~= _returnMenu;

			MenuItemConnector.connectVertical( _items );
			_activeItem = _playMenu.activate();
			
			int xx = WIDTH / 2 , yy = HEIGHT / 2;
			_playMenu.moveTo(   xx , yy +=  20 );
			_saveMenu.moveTo(   xx , yy +=  20 );
			_loadMenu.moveTo(   xx , yy +=  20 );
			_clsMenu.moveTo(    xx , yy +=  20 );
			_titleMenu.moveTo(  xx , yy +=  20 );
			_returnMenu.moveTo( xx , yy +=  20 );
		
			_playMenu.setSelectHandler( &onPlayMenuSelect );
			_saveMenu.setSelectHandler( &onSaveMenuSelect );
			_loadMenu.setSelectHandler( &onLoadMenuSelect );
			_clsMenu.setSelectHandler(  &onClsMenuSelect );
			_titleMenu.setSelectHandler(  &onTitleMenuSelect );
			_returnMenu.setSelectHandler( &onReturnMenuSelect );
			Console.writeln("OK");
		}
		
	} //

	int _cntY = 0, _cntX = 0;
	public IState action() {
		_frameCnt ++;
		foreach ( int i , IMenuItem item ; _items  ) {
			item.action();
		} //
		if ( _frameCnt > 30 ) {
			if ( _pad.y != 0 )  _cntY++; 
			else					_cntY = 0;
			if ( _pad.x != 0 )  _cntX++;
			else					_cntX = 0;
			if ( _cntY % 10 == 1 ) {
				_activeItem = ( _pad.y > 0 ) ? _activeItem.down  : _activeItem.up;
			}
			if ( _cntX % 10 == 1  || ( _cntX > 60 && _cntX % 3 == 1 ) ) {
				_activeItem = ( _pad.x > 0 ) ? _activeItem.right : _activeItem.left;
			}
		}

		if ( _pad.getButton( 0 ) != 0 || _pad.getButton( 1 ) != 0 ) {
			_activeItem.select();
		}
		

		return _nextState;
	} //

	public IState draw( IGraphics g ) {
		foreach ( int i , IMenuItem item ; _items  ) {
			item.drawingEntry( g );
		} //
		_background.drawingEntry( g );
		g.draw();
		return this;
	} //

	public bool onKeyDown( int keyCode ){
		switch ( keyCode ) {
			case KeyCode.UP:
				_pad.y = -1;
				break;
			case KeyCode.DOWN:
				_pad.y =  1;
				break;
			case KeyCode.LEFT:
				_pad.x = -1;
				break;
			case KeyCode.RIGHT:
				_pad.x =  1;
				break;
			case KeyCode.Z:
			case KeyCode.ENTER:
				_pad.setButton( 0 , 1 );
				break;
			case KeyCode.X:
				_pad.setButton( 1 , 1 );
				break;
			default:
				break;
		}
		return true;
	} //
	public bool onKeyUp( int keyCode ){
		switch ( keyCode ) {
			case KeyCode.UP:
				_pad.y = 0;
				break;
			case KeyCode.DOWN:
				_pad.y = 0;
				break;
			case KeyCode.LEFT:
				_pad.x = 0;
				break;
			case KeyCode.RIGHT:
				_pad.x = 0;
				break;
			case KeyCode.Z:
			case KeyCode.ENTER:
				_pad.setButton( 0 , 0 );
				break;
			case KeyCode.X:
				_pad.setButton( 1 , 0 );
				break;
			case KeyCode.SPACE:
				_returnValue = RETURN;
				_nextState = _editorState.call( this );
				break;
			default:
				break;
		}
		return true;
	} //
	public bool onJoyAxis( int deviceId , int axis , int delta ){
		if ( axis == JOYAXIS_X ) {
			if( delta != 0 ) _pad.x = ( delta > 0 ) ? 1 : -1;
			else			 _pad.x = 0;
		}
		if ( axis == JOYAXIS_Y ){
			if( delta != 0 ) _pad.y = ( delta > 0 ) ? 1 : -1;
			else			 _pad.y = 0;
		}
		return true;
	} //
	
	public bool onJoyButtonDown( int id , int button ) {
		int bm = GameConfig.getButtonMap( button );
		if ( bm == 9 ) { 
			_nextState = _caller.call( this );
			_returnValue = RETURN;
		} else
		_pad.setButton( GameConfig.getButtonMap( button ) , 1 );
		return true;
	} //

	public bool onJoyButtonUp( int id , int button ) {
		_pad.setButton( GameConfig.getButtonMap( button ) , 0 );
		return true;
	} //



	public void onPlayMenuSelect(IMenuItem im) {
		Console.writeln("PLAY!");
		_returnValue = PLAY;
		_selectState.title = "PAUSE";
		_nextState = _editorState.call( this );
	} //

	public void onSaveMenuSelect(IMenuItem im) {
		_selectState.title = "SAVE";
		_returnValue = SAVE;
		_nextState = _selectState.call( this );
	} //

	public void onLoadMenuSelect(IMenuItem im) {
		_returnValue = LOAD;
		_nextState = _selectState.call( this );
		_selectState.title = "LOAD";
	} //

	public void onClsMenuSelect(IMenuItem im) {
		_returnValue = CLS;
		_nextState = _editorState.call( this );
		Console.writeln("CLS!");
	} //

	public void onReturnMenuSelect(IMenuItem im){
		_returnValue = RETURN;
		_nextState = _editorState.call( this );
		Console.writeln("GO TO TITLE!");
	} //
	public void onTitleMenuSelect(IMenuItem im){
		_returnValue = TITLE;
		_nextState = _editorState.call( this );
		Console.writeln("GO TO TITLE!");
	} //


} //

